//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace Microsoft.TridentWordAddIn.Presentation
{
    internal partial class WorkflowChildren : UserControl
    {
        private string workflowName = string.Empty;
        public string WorkflowDescription { get; set; }
        private List<WorkflowChild> children = new List<WorkflowChild>();
        private int selectedChildIndex = -1;
        private TreeNode root;
        private bool readOnly = true;
        private bool disableChildrenBelowSelected;
        private ImageList imageList;
        private string workflowImageKey = "workflow";
        private string[] childImageKeys = new string[] { "child" };
        private string[] selectedChildImageKeys = new string[] { "child" };
        private string[] disabledChildImageKeys = new string[] { "disabledChild" };
        private string message = "No children";
        private bool onlyShowMessage;


        /// <summary>
        /// Event that fires when the selected index of the pipeline 
        /// children changes
        /// </summary>
        public event EventHandler<EventArgs> SelectedIndexChanged;

        /// <summary>
        /// Occurs when [node mouse double click].
        /// </summary>
        public event EventHandler<EventArgs> NodeMouseDoubleClick;

        /// <summary>
        /// Gets or sets the image list.
        /// </summary>
        /// <value>The image list.</value>
        [Browsable(true)]
        public ImageList ImageList
        {
            get { return imageList; }
            set
            {
                imageList = value;
                if (value != null)
                {
                    lvWorkflowChildren.ImageList = value;
                }
                else
                {
                    lvWorkflowChildren.ImageList = imlTreeView;
                }
            }
        }

        /// <summary>
        /// Gets or sets the ToolTip.
        /// </summary>
        /// <value>ToolTip.</value>
        [Browsable(true)]
        [DefaultValue("")]
        public string ToolTip
        {
            get { return toolTip.GetToolTip(lvWorkflowChildren); }
            set { toolTip.SetToolTip(lvWorkflowChildren, value); }
        }

        /// <summary>
        /// Gets or sets the pipeline image key.
        /// </summary>
        /// <value>The pipeline image key.</value>
        [Browsable(true)]
        [DefaultValue("pipeline")]
        public string PipelineImageKey
        {
            get { return workflowImageKey; }
            set { workflowImageKey = value; }
        }

        /// <summary>
        /// Gets the selected node.
        /// </summary>
        /// <value>The selected node.</value>
        [Browsable(true)]
        public TreeNode SelectedNode
        {
            get { return lvWorkflowChildren.SelectedNode; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [hide selection].
        /// </summary>
        /// <value><c>true</c> if [hide selection]; otherwise, <c>false</c>.</value>
        [Browsable(true)]
        public bool HideSelection
        {
            get { return lvWorkflowChildren.HideSelection; }
            set { lvWorkflowChildren.HideSelection = value; }
        }

        /// <summary>
        /// Gets or sets the child image key.
        /// </summary>
        /// <value>The child image key.</value>
        [Browsable(true)]
        [DefaultValue("child")]
        public string ChildImageKey
        {
            get { return string.Join(",", childImageKeys); }
            set
            {
                if (value != null)
                {
                    childImageKeys = value.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                }
                else
                {
                    childImageKeys = new string[0];
                }
            }
        }

        /// <summary>
        /// Gets or sets the selected child image key.
        /// </summary>
        /// <value>The selected child image key.</value>
        [Browsable(true)]
        [DefaultValue("child")]
        public string SelectedChildImageKey
        {
            get { return string.Join(",", selectedChildImageKeys); }
            set
            {
                if (value != null)
                {
                    selectedChildImageKeys = value.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                }
                else
                {
                    selectedChildImageKeys = new string[0];
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkflowChildren"/> class.
        /// </summary>
        public WorkflowChildren()
        {
            InitializeComponent();
            if (DesignMode)
            {
                children.Add(new WorkflowChild("Child", "", "", 0));
            }

            UpdateCall();
        }

        /// <summary>
        /// Gets or sets the name of the pipeline.
        /// </summary>
        /// <value>The name of the pipeline.</value>
        [Browsable(true)]
        [DefaultValue("")]
        public string WorkflowName
        {
            get { return workflowName; }
            set
            {
                workflowName = value;
                UpdateCall();
            }
        }

        /// <summary>
        /// Gets or sets the index of the selected child.
        /// </summary>
        /// <value>The index of the selected child.</value>
        [DefaultValue(-1)]
        public int SelectedChildIndex
        {
            get { return selectedChildIndex; }
            set
            {
                if (value != -1)
                {
                    if (value > -1 && value < children.Count)
                    {
                        selectedChildIndex = value;
                    }
                    else
                    {
                        throw new IndexOutOfRangeException("Selected ChildIndex out of range");
                    }
                }
                else
                {
                    selectedChildIndex = value;
                }
                SetSelectedChild();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [read only].
        /// </summary>
        /// <value><c>true</c> if [read only]; otherwise, <c>false</c>.</value>
        [Browsable(true)]
        [DefaultValue(true)]
        [Description("Indicates if the stop point can be edited or not")]
        public bool ReadOnly
        {
            get { return readOnly; }
            set { readOnly = value; }
        }

        /// <summary>
        /// Gets or sets the child names.
        /// </summary>
        /// <value>The child names.</value>
        [Browsable(false)]
        public WorkflowChild[] Children
        {
            get { return children.ToArray(); }
            set
            {
                children.Clear();
                selectedChildIndex = -1;
                if (value != null)
                {
                    children.AddRange(value);
                }

                UpdateCall();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether children below the selected on shall be 
        ///     shown as disabled.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if children below the selected on shall be 
        ///     shown as disabled; otherwise, <c>false</c>.
        /// </value>
        [Browsable(true)]
        [DefaultValue(false)]
        [Description("Indicates if the child nodes below the selected shall be disabled")]
        public bool DisableChildrenBelowSelected
        {
            get { return disableChildrenBelowSelected; }
            set { disableChildrenBelowSelected = value; }
        }

        /// <summary>
        /// Gets or sets the disabled child image key.
        /// </summary>
        /// <value>The disabled child image key.</value>
        [Browsable(true)]
        [DefaultValue("disabledChild")]
        public string DisabledChildImageKey
        {
            get { return string.Join(",", selectedChildImageKeys); }
            set
            {
                if (value != null)
                {
                    disabledChildImageKeys = value.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                }
                else
                {
                    disabledChildImageKeys = new string[0];
                }
            }
        }

        /// <summary>
        /// Gets or sets the message.
        /// </summary>
        /// <value>The message.</value>
        [Browsable(false)]
        public string Message
        {
            get { return message; }
            set { message = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [only show message].
        /// </summary>
        /// <value><c>true</c> if [only show message]; otherwise, <c>false</c>.</value>
        [Browsable(false)]
        public bool OnlyShowMessage
        {
            get { return onlyShowMessage; }
            set
            {
                if (value != onlyShowMessage)
                {
                    onlyShowMessage = value;
                    UpdateCall();
                }
            }
        }

        /// <summary>
        /// Sets the selected child.
        /// </summary>
        private void SetSelectedChild()
        {
            if (root != null && root.Nodes.Count > 0)
            {
                for (int i = 0; i < root.Nodes.Count; i++)
                {
                    SetNodeImageKey(i, root.Nodes[i]);
                }

                SetSelectedNode();
            }
        }

        /// <summary>
        /// Sets the selected node.
        /// </summary>
        private void SetSelectedNode()
        {
            if (selectedChildIndex != -1 && selectedChildIndex < root.Nodes.Count)
            {
                lvWorkflowChildren.SelectedNode = root.Nodes[selectedChildIndex];
            }
            else
            {
                lvWorkflowChildren.SelectedNode = null;
            }
        }

        /// <summary>
        /// Sets the node image key.
        /// </summary>
        /// <param name="i">The i.</param>
        /// <param name="node">The node.</param>
        private void SetNodeImageKey(int i, TreeNode node)
        {
            WorkflowChild pipelineChild = node.Tag as WorkflowChild;
            if (pipelineChild != null)
            {
                if (disableChildrenBelowSelected
                    && selectedChildIndex != -1
                    && i > selectedChildIndex)
                {
                    node.SelectedImageKey = disabledChildImageKeys[pipelineChild.TypeIndex];
                    node.ImageKey = disabledChildImageKeys[pipelineChild.TypeIndex];
                }
                else if (selectedChildIndex == i)
                {
                    node.SelectedImageKey = selectedChildImageKeys[pipelineChild.TypeIndex];
                    node.ImageKey = selectedChildImageKeys[pipelineChild.TypeIndex];
                }
                else
                {
                    node.SelectedImageKey = childImageKeys[pipelineChild.TypeIndex];
                    node.ImageKey = childImageKeys[pipelineChild.TypeIndex];
                }
            }
        }

        /// <summary>
        /// Updates the user control UI.
        /// </summary>
        public void UpdateCall()
        {
            lvWorkflowChildren.Nodes.Clear();

            if (onlyShowMessage)
            {
                root = new TreeNode(message);
                lvWorkflowChildren.Nodes.Add(root);
                return;
            }

            if (workflowName == null || string.IsNullOrEmpty(workflowName))
            {
                lvWorkflowChildren.Enabled = false;
                return;
            }

            lvWorkflowChildren.Enabled = true;
            root = new TreeNode(workflowName);
            root.ToolTipText = this.WorkflowDescription;
            root.ImageKey = workflowImageKey;
            root.SelectedImageKey = workflowImageKey;
            lvWorkflowChildren.Nodes.Add(root);

            for (int i = 0; i < children.Count; i++)
            {
                WorkflowChild workflowChild = children[i];
                TreeNode step = new TreeNode(workflowChild.Label);
                step.ToolTipText = workflowChild.Description;
                step.Tag = workflowChild;
                root.Nodes.Add(step);
                SetNodeImageKey(i, step);
            }

            SetSelectedNode();
            root.Expand();
        }

        /// <summary>
        /// Handles the AfterSelect event of the PipelineStepsTreeView control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeViewEventArgs"/> instance containing the event data.</param>
        private void PipelineStepsTreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            try
            {
                if (!readOnly && lvWorkflowChildren.SelectedNode != null)
                {
                    if (e.Node != root)
                    {
                        int newIndex = e.Node.Index;
                        bool actualChange = (selectedChildIndex != newIndex);
                        selectedChildIndex = newIndex;
                        SetSelectedChild();

                        if (actualChange)
                        {
                            OnSelectedIndexChanged();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, this);
            }
        }

        /// <summary>
        /// Called when selected index changes.
        /// </summary>
        protected void OnSelectedIndexChanged()
        {
            if (SelectedIndexChanged != null)
            {
                SelectedIndexChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Called when [node mouse double click].
        /// </summary>
        protected void OnNodeMouseDoubleClick()
        {

            if (NodeMouseDoubleClick != null)
            {
                NodeMouseDoubleClick(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the BeforeCollapse event of the PipelineStepsTreeView control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeViewCancelEventArgs"/> instance containing the event data.</param>
        private void PipelineStepsTreeView_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            e.Cancel = true;
        }

        /// <summary>
        /// Handles the BeforeExpand event of the PipelineStepsTreeView control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeViewCancelEventArgs"/> instance containing the event data.</param>
        private void PipelineStepsTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            if (e.Node != root)
                e.Cancel = true;
        }

        /// <summary>
        /// Handles the NodeMouseDoubleClick event of the PipelineChildrenTreeView control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeNodeMouseClickEventArgs"/> instance containing the event data.</param>
        private void PipelineChildrenTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            OnDoubleClick(EventArgs.Empty);
            if (e.Node != root)
            {
                OnNodeMouseDoubleClick();
            }
        }

        /// <summary>
        /// Begins the update.
        /// </summary>
        public void BeginUpdate()
        {
            lvWorkflowChildren.BeginUpdate();
        }

        /// <summary>
        /// Ends the update.
        /// </summary>
        public void EndUpdate()
        {
            lvWorkflowChildren.EndUpdate();
        }
    }

    /// <summary>
    /// Pipeline Child Class
    /// </summary>
    public class WorkflowChild
    {
        /// <summary>
        /// Gets or sets the label.
        /// </summary>
        /// <value>The label.</value>
        public string Label { get; set; }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        /// <value>The description.</value>
        public string Description { get; set; }

        /// <summary>
        /// Gets or sets the index of the type.
        /// </summary>
        /// <value>The index of the type.</value>
        public int TypeIndex { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkflowChild"/> class.
        /// </summary>
        public WorkflowChild()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkflowChild"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="label">The label.</param>
        /// <param name="description">The description.</param>
        /// <param name="imageIndex">Index of the image.</param>
        public WorkflowChild(string name, string label, string description, int imageIndex)
        {
            this.Name = name;
            this.Label = label;
            this.Description = description ?? name;
            this.TypeIndex = imageIndex;
        }
    }
}
